﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using libUtilities;

namespace ThresholdModeling
{
    public class k_n_ThresholdModel
    {
        private static String Error = "No"; //Notify error

        private BigInteger M = 0;
        private static String InvisibleSecret = ""; //Be assigned when constructs members information
        //Then may be filled in the routing servers password
        public static String GlobalSecret; //Obtains the recovery secret to be compaired with InvisibleSecret

        private List<int> _thresholdPattern = new List<int>();

        public List<int> ThresholdPattern
        {
            get { return _thresholdPattern; }
        }

        private int _n;

        public int N
        {
            get { return _n; }
            set
            {
                if ((int)value > 0 && (int)value <= GlobalConstant.MaxNumbersOfMemberPerGroup)
                    _n = value;
                else
                {
                    //Do something to notify
                    _k = 0;
                    Error = "Number of Clients is over the allowed numbers!";
                }
            }
        }

        private int _k;

        public int K
        {
            get { return _k; }
            set
            {
                if ((int)value > 0 && (int)value < _n)
                    _k = value;
                else
                {
                    //Do something to notify
                    _n = 0;
                    Error = "Minimum Clients is over the numbers of Clients!";
                }
            }
        }

        public k_n_ThresholdModel()
        {
            this._n = this._k = 0;
        }

        public k_n_ThresholdModel(int n, int k)
        {
            this._n = n;
            this._k = k;
        }

        public k_n_ThresholdModel(int n, int k, String secret, BigInteger bigM)
        {
            this._n = n;
            this._k = k;
            InvisibleSecret = secret;
            M = bigM;
        }

        private static void Reset()
        {
            Error = "No";
        }

        public static String GetError()
        {
            String error = Error;
            if (!"No".Equals(error))
            {
                Error = "No";
                Reset();
                return error;
            }

            return "No";
        }

        public void ConstructThresholdPattern()
        {
            if (_thresholdPattern.Count == 0)
                _thresholdPattern = Cryptography.FindAllPrime(1000, 10000);
        }

        public void GetPattern(List<int> pattern)
        {
            _thresholdPattern.AddRange(pattern);
        }

        public List<int> ConstructThreholds(ref BigInteger min, ref BigInteger max)
        {
            List<int> th = new List<int>();
            min = 1;
            max = 1;
            //int count = 0;
            int minIndex = _k;
            int maxIndex = _n - _k + 2;

            do
            {
                th.Clear();
                Random r = new Random();
                int idx = r.Next(0, _thresholdPattern.Count - 1);
                int j = 1;
                while (j <= _n)
                {
                    if (j <= minIndex)
                        min *= _thresholdPattern[idx];
                    if (j >= maxIndex)
                        max *= _thresholdPattern[idx];
                    th.Add(_thresholdPattern[idx]);
                    j++;
                    idx++;
                }
            }
            while (min <= max);

            if (th.Count == 0)
                return null;

            return th;
        }

        public bool ConstructSecret(List<MemberInfo> members)
        {
            try
            {
                ConstructThresholdPattern();
                BigInteger min, max;
                min = max = 1;
                List<int> thresholds = this.ConstructThreholds(ref min, ref max);
                //this.Secret = (min + max) / 2;
                BigInteger s = (min + max) / 2;

                //InvisibleSecret = s.ToString();
                InvisibleSecret = Cryptography.CreateHash(s.ToString());

                //Construct M
                BigInteger M = 1;
                for (int i = 0; i < _n; i++)
                    M *= (BigInteger)thresholds[i];
                //Construct Ii
                for (int i = 0; i < this._n; i++)
                {
                    int I = (int)(s % thresholds[i]);

                    members[i].GetInfo(I, thresholds[i], M, InvisibleSecret);
                }
                return true;
            }
            catch (Exception ex)
            {
                Error = ex.Message;
                GlobalSecret = InvisibleSecret = "";
                return false;
            }

        }

        public bool RecoverySecretAndChecking(List<MemberInfo> mems)
        {
            BigInteger Mi, SecretTemp;
            //InvisibleSecret = mems[0].Group.HashSecret;
            if (InvisibleSecret == null || "".Equals(InvisibleSecret))
                return false;
            if (this.M == 0)
                return false;

            SecretTemp = 0;
            BigInteger modMul = 1;
            int Ni;
            for (int i = 0; i < mems.Count; i++)
            {
                Mi = M / mems[i].mi;
                Ni = Cryptography.ModInverseIn_Zm(Mi, mems[i].mi);

                SecretTemp += mems[i].I * Mi * Ni;
                modMul *= mems[i].mi;
            }
            SecretTemp = SecretTemp % (modMul);

            String hst = Cryptography.CreateHash(SecretTemp.ToString());
            //String hst = SecretTemp.ToString();

            if (!hst.Equals(InvisibleSecret))
                return false;
            return true;
        }

        public String RecoverySecret(List<MemberInfo> mems)
        {
            BigInteger Mi, SecretTemp;
            //InvisibleSecret = mems[0].Group.HashSecret;

            if (this.M == 0)
                return null;

            SecretTemp = 0;
            BigInteger modMul = 1;
            int Ni;
            for (int i = 0; i < mems.Count; i++)
            {
                Mi = M / mems[i].mi;
                Ni = Cryptography.ModInverseIn_Zm(Mi, mems[i].mi);

                SecretTemp += mems[i].I * Mi * Ni;
                modMul *= mems[i].mi;
            }
            SecretTemp = SecretTemp % (modMul);

            String hst = Cryptography.CreateHash(SecretTemp.ToString());
            //String hst = SecretTemp.ToString();

            return hst;
        }

    }
}
